package es.us.lsi.daw.dao.impl;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.codehaus.jackson.map.ObjectMapper;
import org.hibernate.Criteria;
import org.hibernate.FetchMode;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.NonUniqueObjectException;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.ModelMap;

import es.us.lsi.daw.annotations.FechaDesde;
import es.us.lsi.daw.annotations.FechaHasta;
import es.us.lsi.daw.annotations.Maximo;
import es.us.lsi.daw.annotations.Minimo;
import es.us.lsi.daw.annotations.NoFiltrar;
import es.us.lsi.daw.dao.DAOComun;
import es.us.lsi.daw.model.util.BaseEntity;
import es.us.lsi.daw.servicios.MessageService;
import es.us.lsi.daw.util.Acronimo;
import es.us.lsi.daw.util.Util;


@Transactional
public class DAOComunImpl implements DAOComun {

	public static final int		PAGE_SIZE	= 5;
	public static final int	SECONDS2MILLIS_FACTOR	= 1000;

	@Autowired
	protected SessionFactory	sessionFactory;
	
	@Autowired
	MessageService mensaje;
	
	@Autowired
	ObjectMapper objectMapper;

	protected Logger			log			= LoggerFactory.getLogger(this.getClass());

	/* --------------- GUARDAR --------------- */

	/*
	 * (non-Javadoc)
	 * @see dinamicarea.plantilla.dao.Application#guardarObjeto(java.lang.Object)
	 */
	public <T> boolean guardarObjeto(T obj) throws DataAccessException {

		try {
			sessionFactory.getCurrentSession().saveOrUpdate(obj);
			return true;
		} catch (NonUniqueObjectException e) {
			log.info("NonUniqueObjectException", e);
			try {
				sessionFactory.getCurrentSession().clear();
				sessionFactory.getCurrentSession().saveOrUpdate(obj);
				return true;
			} catch (HibernateException e1) {
				log.error("Error at guardarObjeto", e);
				return false;
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * @see dinamicarea.plantilla.dao.Application#guardarListaObjetos(java.util.Collection)
	 */
	public <T> void guardarListaObjetos(Collection<T> cllLista) throws DataAccessException {

		for (Object l : cllLista) {
			try {
				sessionFactory.getCurrentSession().saveOrUpdate(l);
			} catch (NonUniqueObjectException ex) {
				log.info("NonUniqueObjectException", ex);
				sessionFactory.getCurrentSession().clear();
				sessionFactory.getCurrentSession().saveOrUpdate(l);
			} catch (HibernateException e) {
				log.error("Error at guardarListaObjetos", e);
			}
		}
	}

	/* --------------- BORRAR --------------- */

	/*
	 * (non-Javadoc)
	 * @see dinamicarea.plantilla.dao.Application#borrarObjeto(java.lang.Object)
	 */
	public <T> boolean borrarObjeto(T obj) throws DataAccessException {

		try {
			sessionFactory.getCurrentSession().delete(obj);
			return true;
		} catch (Exception e) {
			log.error("Error at borrarObjeto", e);
			return false;
		}
	}

	/*
	 * (non-Javadoc)
	 * @see dinamicarea.plantilla.dao.Application#borrarObjetoId(java.lang.Class, java.lang.Long)
	 */
	public <T extends BaseEntity> boolean borrarObjetoId(Class<T> clase, Long id) {

		try {
			Object obj = new Object();
			obj = sessionFactory.getCurrentSession().createCriteria(clase).add(Restrictions.eq("id", id)).uniqueResult();
			if (obj != null) {
				sessionFactory.getCurrentSession().delete(obj);
				return true;
			}
			return false;
		} catch (HibernateException e) {
			log.error("Error at borrarObjetoId", e);
			e.printStackTrace();
			return false;
		}
	}

	/*
	 * (non-Javadoc)
	 * @see dinamicarea.plantilla.dao.Application#borrarObjetos(java.util.Collection)
	 */
	public <T> boolean borrarObjetos(Collection<T> cllObjs) throws DataAccessException {

		try {
			for (Object obj : cllObjs) {
				sessionFactory.getCurrentSession().delete(obj);
			}
			return true;
		} catch (Exception e) {
			log.error("Error at borrarObjetos", e);
			return false;
		}
	}

	/* --------------- LISTADOS --------------- */

	/*
	 * (non-Javadoc)
	 * @see dinamicarea.plantilla.dao.Application#getListaClase(java.lang.Class)
	 */
	@Transactional(readOnly = true)
	public <T> List<T> getListaClase(Class<T> clase) throws DataAccessException {

		return getListaClase(clase, null);
	}

	/*
	 * (non-Javadoc)
	 * @see dinamicarea.plantilla.dao.Application#getListaClase(java.lang.Class, java.lang.String)
	 */
	@SuppressWarnings("unchecked")
	@Transactional(readOnly = true)
	public <T> List<T> getListaClase(Class<T> clase, String lista) throws DataAccessException {

		Order orden = null;
		Criteria crt = sessionFactory.getCurrentSession().createCriteria(clase);
		try {
			orden = ((BaseEntity) clase.newInstance()).getOrder();
		} catch (Exception e) {}
		if (orden != null)
			crt.addOrder(orden);
		if (lista != null)
			crt.setFetchMode(lista, FetchMode.JOIN);
		return crt.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY).list();
	}

	public ModelMap filtroGenerico(Object filtro, Long pagina, String atributo, Order orden) {

		return filtroGenerico(filtro, pagina, atributo, orden, null);
	}

	public ModelMap filtroGenerico(Object filtro, Long pagina, String atributo, Order orden, Class claseBusqueda) {

		return filtroGenerico(filtro, pagina, atributo, orden, claseBusqueda, null);
	}

	/*
	 * (non-Javadoc)
	 * @see dinamicarea.plantilla.dao.Application#filtroGenerico(java.lang.Object, java.lang.Long, java.lang.String,
	 * org.hibernate.criterion.Order)
	 */
	@SuppressWarnings("unchecked")
	@Transactional(readOnly = true)
	public ModelMap filtroGenerico(Object filtro, Long pagina, String atributo, Order orden, Class claseBusqueda, Integer numMaxRegistros) {

		List<Field> lstCampos = new ArrayList<Field>();
		Object fechaDesde = null;
		Object fechaHasta = null;
		String campoReferencia = null;
		Class claseCriteria = null;

		for (Field f : filtro.getClass().getDeclaredFields()) {
			lstCampos.add(f);
		}
		Class clase = filtro.getClass();
		int sw = 0;
		while (sw == 0) {
			if (clase != null && clase.getSuperclass() != null) {
				for (Field f : clase.getSuperclass().getDeclaredFields()) {
					lstCampos.add(f);
				}
				clase = clase.getSuperclass();
			} else
				sw = 1;
		}
		long tam = 0;

		if (claseBusqueda != null)
			claseCriteria = claseBusqueda;
		else
			claseCriteria = filtro.getClass();
		Criteria crt = sessionFactory.getCurrentSession().createCriteria(claseCriteria);
		Criteria crtTam = sessionFactory.getCurrentSession().createCriteria(claseCriteria);

		for (Field f : lstCampos) {
			try {
				f.setAccessible(true);
				if (!f.isAnnotationPresent(NoFiltrar.class) && !f.toString().contains("static")) {
					if (f.get(filtro) != null && !f.getType().getSimpleName().equals("List") && !f.getType().getSimpleName().equals("Collection") && !f.isAnnotationPresent(FechaDesde.class)
							&& !f.isAnnotationPresent(FechaHasta.class)) {
						if (f.isAnnotationPresent(Maximo.class)) {
							Maximo max = f.getAnnotation(Maximo.class);
							if (max != null) {
								String campo = "";
								if (!max.campo().isEmpty())
									campo = max.campo();
								else
									campo = f.getName();
								if (max.tipo() == Maximo.Estricto) {
									crt.add(Restrictions.lt(campo, f.get(filtro)));
									crtTam.add(Restrictions.lt(campo, f.get(filtro)));
								} else {
									crt.add(Restrictions.le(campo, f.get(filtro)));
									crtTam.add(Restrictions.le(campo, f.get(filtro)));
								}
							}
						} else if (f.isAnnotationPresent(Minimo.class)) {
							Minimo min = f.getAnnotation(Minimo.class);
							if (min != null) {
								String campo = "";
								if (!min.campo().isEmpty())
									campo = min.campo();
								else
									campo = f.getName();
								if (min.tipo() == Minimo.Estricto) {
									crt.add(Restrictions.gt(campo, f.get(filtro)));
									crtTam.add(Restrictions.gt(campo, f.get(filtro)));
								} else {
									crt.add(Restrictions.ge(campo, f.get(filtro)));
									crtTam.add(Restrictions.ge(campo, f.get(filtro)));
								}
							}
						} else {
							if (f.getType().getSimpleName().equals("int")) {
								if (Integer.parseInt(f.get(filtro).toString()) != 0) {
									crt.add(Restrictions.eq(f.getName(), f.get(filtro)));
									crtTam.add(Restrictions.eq(f.getName(), f.get(filtro)));
								}
							} else if (f.getType().getSimpleName().equals("String")) {
								if (!f.get(filtro).toString().isEmpty()) {
									crt.add(Restrictions.ilike(f.getName(), f.get(filtro).toString(), MatchMode.ANYWHERE));
									crtTam.add(Restrictions.eq(f.getName(), f.get(filtro)));
								}
							} else {
								crt.add(Restrictions.eq(f.getName(), f.get(filtro)));
								crtTam.add(Restrictions.eq(f.getName(), f.get(filtro)));
							}
						}
					} else if (f.get(filtro) != null && (f.getType().getSimpleName().equals("List") || f.getType().getSimpleName().equals("Collection"))) {
						List<Long> lstIdentificadores = new ArrayList<Long>();
						for (Object o : (List) f.get(filtro)) {
							if (o instanceof BaseEntity)
								lstIdentificadores.add(((BaseEntity) o).getId());
						}
						if (!lstIdentificadores.isEmpty()) {
							crt.createAlias(f.getName(), f.getName());
							crtTam.createAlias(f.getName(), f.getName());
							crt.add(Restrictions.in(f.getName() + ".id", lstIdentificadores));
							crtTam.add(Restrictions.in(f.getName() + ".id", lstIdentificadores));
						}
					} else if (f.get(filtro) != null && f.isAnnotationPresent(FechaDesde.class)) {
						fechaDesde = f.get(filtro);
						if (fechaHasta != null) {
							if (campoReferencia == null) {
								FechaDesde fechaDesdeAnnotation = f.getAnnotation(FechaDesde.class);
								campoReferencia = fechaDesdeAnnotation.campoReferencia();
							}
							crt.add(Restrictions.between(campoReferencia, fechaDesde, fechaHasta));
							crtTam.add(Restrictions.between(campoReferencia, fechaDesde, fechaHasta));
						}
					} else if (f.get(filtro) != null && f.isAnnotationPresent(FechaHasta.class)) {
						fechaHasta = f.get(filtro);
						if (fechaDesde != null) {
							if (campoReferencia == null) {
								FechaHasta fechaHastaAnnotation = f.getAnnotation(FechaHasta.class);
								campoReferencia = fechaHastaAnnotation.campoReferencia();
							}
							crt.add(Restrictions.between(campoReferencia, fechaDesde, fechaHasta));
							crtTam.add(Restrictions.between(campoReferencia, fechaDesde, fechaHasta));
						}
					}
				}
			} catch (IllegalArgumentException e) {
				log.error("Error at filtroGenerico", e);
			} catch (Exception e) {
				log.error("Error at filtroGenerico", e);
			}
		}

		if (orden != null)
			crt.addOrder(orden);
		tam = tamFiltro(crtTam);
		ModelMap map = new ModelMap();
		String nombreLista = Acronimo.LIST + claseCriteria.getSimpleName().substring(0, 1).toUpperCase() + claseCriteria.getSimpleName().substring(1).toLowerCase();
		long totalPages;
		long i;
		if (numMaxRegistros == null)
			numMaxRegistros = DAOComunImpl.PAGE_SIZE;
		i = tam / numMaxRegistros;
		if (tam % numMaxRegistros != 0)
			i++;
		totalPages = i;
		if (pagina > totalPages)
			pagina = totalPages;

		List<Object> lstObjetos = new ArrayList<Object>();
		if (atributo != null) {
			crt.setFetchMode(atributo, FetchMode.JOIN).setProjection(Projections.distinct(Projections.property("id")));
			List<Long> lst = crt.setMaxResults(numMaxRegistros).setFirstResult(numMaxRegistros * (pagina.intValue() - 1)).list();
			if (lst != null && !lst.isEmpty()) {
				lstObjetos = sessionFactory.getCurrentSession().createCriteria(claseCriteria).add(Restrictions.in("id", lst)).setFetchMode(atributo, FetchMode.JOIN)
						.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY).list();
			}
		} else {
			lstObjetos = crt.setMaxResults(numMaxRegistros).setFirstResult(numMaxRegistros * (pagina.intValue() - 1)).list();
		}
		if (lstObjetos != null && lstObjetos.size() > 0) {
			HashSet<Object> cnjObjetosAux = new HashSet<Object>();
			cnjObjetosAux.addAll(lstObjetos);
			lstObjetos.clear();
			lstObjetos.addAll(cnjObjetosAux);
			if (orden != null) {
				String campoOrden = orden.toString().substring(0, orden.toString().indexOf(" "));
				String campoDireccion = orden.toString().substring(orden.toString().indexOf(" ") + 1);
				if (!campoOrden.isEmpty() && campoDireccion.equals("desc")) {
					Util.ordenaDescendente(lstObjetos, campoOrden);
				} else if (!campoOrden.isEmpty() && campoDireccion.equals("asc")) {
					Util.ordenaAscendente(lstObjetos, campoOrden);
				}
			}
		}
		map.put(nombreLista, (List<Object>) lstObjetos);
		map.put("totalPages", totalPages);
		map.put("pagina", pagina);
		map.put("registrosTotales", tam);
		map.put("registrosMostrados", lstObjetos.size());

		return map;
	}

	/*
	 * (non-Javadoc)
	 * @see dinamicarea.plantilla.dao.Application#filtroGenerico(java.lang.Object, java.lang.String)
	 */
	@SuppressWarnings("unchecked")
	public <T> List<T> filtroGenerico(Object filtro, String atributo) {

		try {
			Criteria crt = busquedaGenerica(filtro, atributo);
			return crt.list();
		} catch (HibernateException e) {
			log.error("Error at filtroGenerico", e);
			return null;
		}
	}

	/**
	 * @param filtro
	 * @param atributo
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private Criteria busquedaGenerica(Object filtro, String atributo) {

		List<Field> lstCampos = new ArrayList<Field>();
		for (Field f : filtro.getClass().getDeclaredFields()) {
			lstCampos.add(f);
		}
		Class clase = filtro.getClass();
		int sw = 0;
		while (sw == 0) {
			if (clase != null && clase.getSuperclass() != null) {
				for (Field f : clase.getSuperclass().getDeclaredFields()) {
					lstCampos.add(f);
				}
				clase = clase.getSuperclass();
			} else
				sw = 1;
		}
		Criteria crt = sessionFactory.getCurrentSession().createCriteria(filtro.getClass());
		for (Field f : lstCampos) {
			try {
				f.setAccessible(true);
				if (f.get(filtro) != null && !f.getType().getSimpleName().equals("List") && !f.getType().getSimpleName().equals("Collection") && !f.isAnnotationPresent(NoFiltrar.class)
						&& !f.toString().contains("static")) {
					if (f.isAnnotationPresent(Maximo.class)) {
						Maximo max = f.getAnnotation(Maximo.class);
						if (max != null) {
							String campo = "";
							if (!max.campo().isEmpty())
								campo = max.campo();
							else
								campo = f.getName();
							if (max.tipo() == Maximo.Estricto)
								crt.add(Restrictions.lt(campo, f.get(filtro)));
							else
								crt.add(Restrictions.le(campo, f.get(filtro)));
						}
					} else if (f.isAnnotationPresent(Minimo.class)) {
						Minimo min = f.getAnnotation(Minimo.class);
						if (min != null) {
							String campo = "";
							if (!min.campo().isEmpty())
								campo = min.campo();
							else
								campo = f.getName();
							if (min.tipo() == Minimo.Estricto)
								crt.add(Restrictions.gt(campo, f.get(filtro)));
							else
								crt.add(Restrictions.ge(campo, f.get(filtro)));
						}
					} else {
						if (f.getType().getSimpleName().equals("int")) {
							if (Integer.parseInt(f.get(filtro).toString()) != 0)
								crt.add(Restrictions.eq(f.getName(), f.get(filtro)));
						} else if (f.getType().getSimpleName().equals("String")) {
							if (!f.get(filtro).toString().isEmpty())
								crt.add(Restrictions.ilike(f.getName(), f.get(filtro).toString(), MatchMode.ANYWHERE));
						} else
							crt.add(Restrictions.eq(f.getName(), f.get(filtro)));
					}
				}

			} catch (IllegalArgumentException e) {
				log.error("Error at busquedaGenerica", e);
			} catch (Exception e) {
				log.error("Error at busquedaGenerica", e);
			}
		}

		if (atributo != null) {
			crt.setFetchMode(atributo, FetchMode.JOIN).setProjection(Projections.distinct(Projections.property("id")));
			List<Long> lst = crt.list();
			if (lst != null && !lst.isEmpty()) {
				return sessionFactory.getCurrentSession().createCriteria(filtro.getClass()).add(Restrictions.in("id", lst)).setFetchMode(atributo, FetchMode.JOIN)
						.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
			}
		}
		crt.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
		return crt;
	}

	/**
	 * @param c
	 * @return
	 */
	private long tamFiltro(Criteria c) {

		Long tamFiltro = new Long(0);
		List<?> lst = c.setProjection(Projections.rowCount()).list();
		if (!lst.isEmpty())
			tamFiltro = (Long) lst.get(0);
		return tamFiltro;
	}

	/*
	 * (non-Javadoc)
	 * @see dinamicarea.plantilla.dao.Application#filtroGenerico(java.lang.Object, java.lang.Long, java.lang.String)
	 */
	@SuppressWarnings("unchecked")
	public ModelMap filtroGenerico(Object filtro, Long pagina, String atributo) {

		Criteria crt = filtroGenericoCriteria(filtro, pagina, atributo, null);
		long tam = tamFiltro(crt);
		// Evitamos que nos devuelva el número de elementos,
		// en vez de los elementos en si.
		crt.setProjection(null);
		ModelMap map = new ModelMap();
		String nombreLista = Acronimo.LIST + filtro.getClass().getSimpleName().substring(0, 1).toUpperCase() + filtro.getClass().getSimpleName().substring(1).toLowerCase();
		long totalPages;
		long i;
		i = tam / PAGE_SIZE;
		if (tam % PAGE_SIZE != 0)
			i++;
		totalPages = i;
		if (pagina > totalPages)
			pagina = totalPages;

		List<Object> lstObjetos = null;
		if (atributo != null) {
			crt.setFetchMode(atributo, FetchMode.JOIN).setProjection(Projections.distinct(Projections.property("id")));
			List<Long> lst = crt.setMaxResults(PAGE_SIZE).setFirstResult(PAGE_SIZE * (pagina.intValue() - 1)).list();
			if (lst != null && !lst.isEmpty()) {
				lstObjetos = sessionFactory.getCurrentSession().createCriteria(filtro.getClass()).add(Restrictions.in("id", lst)).setFetchMode(atributo, FetchMode.JOIN)
						.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY).list();
			}
		} else
			lstObjetos = crt.setMaxResults(PAGE_SIZE).setFirstResult(PAGE_SIZE * (pagina.intValue() - 1)).list();

		map.put(nombreLista, lstObjetos);
		map.put("totalPages", totalPages);
		map.put("pagina", pagina);

		return map;
	}

	/*
	 * (non-Javadoc)
	 * @see dinamicarea.plantilla.dao.Application#getSizeClase(java.lang.Class)
	 */
	@Transactional(readOnly = true)
	public <T> Long getSizeClase(Class<T> clase) throws DataAccessException {

		List<?> lst = sessionFactory.getCurrentSession().createCriteria(clase).setProjection(Projections.rowCount()).list();
		Long i = new Long(0);
		if (!lst.isEmpty())
			i = (Long) lst.get(0);
		return i;
	}

	/*
	 * (non-Javadoc)
	 * @see dinamicarea.plantilla.dao.Application#obtenerObjetoId(java.lang.Class, java.lang.Long, java.lang.String)
	 */
	public <T> T obtenerObjetoId(Class<T> clase, Long id, String lista) {

		try {
			Criteria crit = sessionFactory.getCurrentSession().createCriteria(clase).add(Restrictions.eq("id", id));
			if (lista != null && !lista.isEmpty())
				crit.setFetchMode(lista, FetchMode.JOIN).setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
			return clase.cast(crit.uniqueResult());
		} catch (HibernateException e) {
			log.error("Error at obtenerObjetoId", e);
			return null;
		}
	}

	/*
	 * (non-Javadoc)
	 * @see dinamicarea.plantilla.dao.Application#obtenerObjetoId(java.lang.Class, java.lang.Long)
	 */
	public <T> T obtenerObjetoId(Class<T> clase, Long id) {

		return obtenerObjetoId(clase, id, null);
	}

	/*
	 * (non-Javadoc)
	 * @see dinamicarea.plantilla.dao.Application#existe(java.lang.Object, java.lang.Object, java.lang.String)
	 */
	@SuppressWarnings("unchecked")
	public boolean existe(Object o, Object valor, String atributo) {

		List<Object> lstRes = sessionFactory.getCurrentSession().createCriteria(o.getClass()).add(Restrictions.eq(atributo, valor)).setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY)
				.list();
		if (lstRes != null && lstRes.size() > 0) {
			if (((BaseEntity) o).getId() == null)
				return true;
			else if (lstRes.size() == 1 && ((BaseEntity) o).getId().equals(((BaseEntity) lstRes.get(0)).getId()))
				return false;
			else
				return true;
		} else
			return false;
	}
	
	public <T> boolean existeClase(Class<T> clase, Object valor, String atributo){
		List<?> lstRes = sessionFactory.getCurrentSession()
				.createCriteria(clase)
				.add(Restrictions.eq(atributo, valor))
				.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY)
				.list();
		
		return !lstRes.isEmpty();
	}

	/*
	 * (non-Javadoc)
	 * @see dinamicarea.plantilla.dao.Application#filtroGenericoUnico(java.lang.Object, java.lang.String)
	 */
	public Object filtroGenericoUnico(Object filtro, String atributo) {

		try {
			Criteria crt = busquedaGenerica(filtro, atributo);
			return crt.uniqueResult();
		} catch (HibernateException e) {
			log.error("Error at filtroGenericoUnico", e);
			return null;
		}
	}

	/*
	 * (non-Javadoc)
	 * @see dinamicarea.plantilla.dao.Application#obtenerObjetoIdConListas(java.lang.Class, java.lang.Long,
	 * java.util.List)
	 */
	public <T> T obtenerObjetoIdConListas(Class<T> clase, Long id, List<String> lstAtributos) {

		T o = obtenerObjetoId(clase, id);
		if (o != null && lstAtributos != null && !lstAtributos.isEmpty()) {
			for (String atributo : lstAtributos) {
				Field f = Util.obtenerCampoClaseSuperClases(o.getClass(), atributo);
				try {
					if (f != null) {
						Hibernate.initialize(f.get(o));
					}
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				}
			}
		}
		return o;
	}

	/**
	 * @param filtro
	 * @param pagina
	 * @param atributo
	 * @param orden
	 * @return
	 */
	private Criteria filtroGenericoCriteria(Object filtro, Long pagina, String atributo, Order orden) {

		List<Field> lstCampos = new ArrayList<Field>();
		for (Field f : filtro.getClass().getDeclaredFields()) {
			lstCampos.add(f);
		}
		Class clase = filtro.getClass();
		int sw = 0;
		while (sw == 0) {
			if (clase != null && clase.getSuperclass() != null) {
				for (Field f : clase.getSuperclass().getDeclaredFields()) {
					lstCampos.add(f);
				}
				clase = clase.getSuperclass();
			} else
				sw = 1;
		}
		sessionFactory.getCurrentSession().clear();
		sessionFactory.getCurrentSession().flush();
		Criteria ctrConsulta = sessionFactory.getCurrentSession().createCriteria(filtro.getClass());
		for (Field f : lstCampos) {
			try {
				f.setAccessible(true);
				if (!f.isAnnotationPresent(NoFiltrar.class) && !f.toString().contains("static")) {
					if (f.get(filtro) != null && !f.getType().getSimpleName().equals("List") && !f.getType().getSimpleName().equals("Collection")) {
						if (f.isAnnotationPresent(Maximo.class)) {
							Maximo max = f.getAnnotation(Maximo.class);
							if (max != null) {
								String campo = "";
								if (!max.campo().isEmpty())
									campo = max.campo();
								else
									campo = f.getName();
								if (max.tipo() == Maximo.Estricto) {
									ctrConsulta.add(Restrictions.lt(campo, f.get(filtro)));
								} else {
									ctrConsulta.add(Restrictions.le(campo, f.get(filtro)));
								}
							}
						} else if (f.isAnnotationPresent(Minimo.class)) {
							Minimo min = f.getAnnotation(Minimo.class);
							if (min != null) {
								String campo = "";
								if (!min.campo().isEmpty())
									campo = min.campo();
								else
									campo = f.getName();
								if (min.tipo() == Minimo.Estricto) {
									ctrConsulta.add(Restrictions.gt(campo, f.get(filtro)));
								} else {
									ctrConsulta.add(Restrictions.ge(campo, f.get(filtro)));
								}
							}
						} else {
							if (f.getType().getSimpleName().equals("int")) {
								if (Integer.parseInt(f.get(filtro).toString()) != 0) {
									ctrConsulta.add(Restrictions.eq(f.getName(), f.get(filtro)));
								}
							} else if (f.getType().getSimpleName().equals("String")) {
								if (!f.get(filtro).toString().isEmpty()) {
									ctrConsulta.add(Restrictions.ilike(f.getName(), f.get(filtro).toString(), MatchMode.ANYWHERE));
								}
							} else {
								ctrConsulta.add(Restrictions.eq(f.getName(), f.get(filtro)));
							}
						}
					}

				}
			} catch (IllegalArgumentException e) {
				log.error("Error at filtroGenericoCriteria", e);
			} catch (Exception e) {
				log.error("Error at filtroGenericoCriteria", e);
			}
		}

		if (orden != null)
			ctrConsulta.addOrder(orden);

		return ctrConsulta;
	}

	public SessionFactory getSessionFactory() {
		return this.sessionFactory;
	}

	public String getCurrentUsername(){
		Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
		String result = null;
		if (principal instanceof UserDetails) {
			result = ((UserDetails)principal).getUsername();
		} else {
			result = principal.toString();
		}
		return result;

	}
}
